document.addEventListener('DOMContentLoaded', () => {
  const svgNS = 'http://www.w3.org/2000/svg';
  const canvas = document.getElementById('canvas');
  const addNodeButton = document.getElementById('addNode');

  const gridSize = 20; // 定义网格大小

  let nodes = [];
  let links = [];
  let selectedLink = null;

  addNodeButton.addEventListener('click', addNode);

  function addNode() {
    const newNode = createNode();
    canvas.appendChild(newNode);
    nodes.push(newNode);
    makeDraggable(newNode);
  }

  function createNode() {
    const group = document.createElementNS(svgNS, 'g');
    group.setAttribute('data-id', `node-${nodes.length + 1}`);
    const rect = document.createElementNS(svgNS, 'rect');
    rect.setAttribute('x', 50);
    rect.setAttribute('y', 50);
    rect.setAttribute('width', 100);
    rect.setAttribute('height', 50);
    rect.setAttribute('fill', '#007BFF');
    rect.setAttribute('stroke', '#000');

    const connectors = ['top', 'bottom', 'left', 'right'].map(position => createConnector(position));

    connectors.forEach(connector => group.appendChild(connector));
    group.appendChild(rect);
    group.classList.add('node');
    group.setAttribute('transform', 'translate(0, 0)');

    return group;
  }

  function createConnector(position) {
    const circle = document.createElementNS(svgNS, 'circle');
    circle.setAttribute('r', 5);
    circle.setAttribute('fill', '#FFF');
    circle.setAttribute('stroke', '#000');
    circle.setAttribute('stroke-width', 2);
    circle.classList.add('connector');
    circle.dataset.position = position;

    switch (position) {
      case 'top':
        circle.setAttribute('cx', 100);
        circle.setAttribute('cy', 45);
        break;
      case 'bottom':
        circle.setAttribute('cx', 100);
        circle.setAttribute('cy', 105);
        break;
      case 'left':
        circle.setAttribute('cx', 45);
        circle.setAttribute('cy', 75);
        break;
      case 'right':
        circle.setAttribute('cx', 155);
        circle.setAttribute('cy', 75);
        break;
    }

    return circle;
  }

  function makeDraggable(node) {
    let offsetX, offsetY;
    let isDragging = false;
    const rect = node.querySelector('rect');

    rect.addEventListener('mousedown', (e) => {
      e.preventDefault();
      const transform = node.getAttribute('transform');
      const translate = transform.match(/translate\(([^,]+),\s*([^\)]+)\)/);
      const startX = translate ? parseFloat(translate[1]) : 0;
      const startY = translate ? parseFloat(translate[2]) : 0;
      offsetX = e.clientX - startX;
      offsetY = e.clientY - startY;
      isDragging = true;
      document.addEventListener('mousemove', onMouseMove);
      document.addEventListener('mouseup', onMouseUp);
    });

    function onMouseMove(e) {
      if (!isDragging) return;
      const newX = e.clientX - offsetX;
      const newY = e.clientY - offsetY;

      // 对齐到最近的网格点
      const snappedX = Math.round(newX / gridSize) * gridSize;
      const snappedY = Math.round(newY / gridSize) * gridSize;

      if (!isNaN(snappedX) && !isNaN(snappedY)) {
        node.setAttribute('transform', `translate(${snappedX}, ${snappedY})`);
        requestAnimationFrame(updateLinks);
      }
    }

    function onMouseUp() {
      isDragging = false;
      document.removeEventListener('mousemove', onMouseMove);
      document.removeEventListener('mouseup', onMouseUp);
    }
  }

  let startConnector = null;

  canvas.addEventListener('click', (e) => {
    if (e.target.classList.contains('connector')) {
      if (startConnector) {
        const endConnector = e.target;
        if (startConnector !== endConnector) {
          createLink(startConnector, endConnector);
          startConnector = null;
        }
      } else {
        startConnector = e.target;
      }
    } else if (e.target.tagName === 'path') {
      selectLink(e.target);
    }
  });

  canvas.addEventListener('contextmenu', (e) => {
    e.preventDefault();
    if (e.target.tagName === 'path') {
      deleteLink(e.target);
    }
  });

  function createLink(startConnector, endConnector) {
    const path = document.createElementNS(svgNS, 'path');
    path.setAttribute('stroke', '#000');
    path.setAttribute('fill', 'none');
    path.dataset.startNodeId = startConnector.parentElement.dataset.id;
    path.dataset.startPosition = startConnector.dataset.position;
    path.dataset.endNodeId = endConnector.parentElement.dataset.id;
    path.dataset.endPosition = endConnector.dataset.position;
    canvas.appendChild(path);
    links.push(path);
    updateLinks();
  }

  function getConnectorCoords(connector) {
    const transform = connector.parentElement.getAttribute('transform');
    const translate = transform.match(/translate\(([^,]+),\s*([^\)]+)\)/);
    if (translate) {
      const parentX = parseFloat(translate[1]);
      const parentY = parseFloat(translate[2]);
      const cx = parseFloat(connector.getAttribute('cx'));
      const cy = parseFloat(connector.getAttribute('cy'));
      return { x: parentX + cx, y: parentY + cy };
    }
    return { x: 0, y: 0 };
  }

  function updateLinks() {
    links.forEach(link => {
      const startNode = document.querySelector(`[data-id="${link.dataset.startNodeId}"]`);
      const endNode = document.querySelector(`[data-id="${link.dataset.endNodeId}"]`);
      const startConnector = startNode.querySelector(`.connector[data-position="${link.dataset.startPosition}"]`);
      const endConnector = endNode.querySelector(`.connector[data-position="${link.dataset.endPosition}"]`);
      const startCoords = getConnectorCoords(startConnector);
      const endCoords = getConnectorCoords(endConnector);

      // 使用 findPath 函数计算避开节点的路径
      const pathData = findPath(startCoords, endCoords);
      link.setAttribute('d', pathData);
    });
  }

  // A*算法实现
  function findPath(start, end) {
    const openSet = [];
    const closedSet = [];
    const gridSize = 10;
    const grid = createGrid(gridSize);

    const startNode = createNodeFromCoords(start, gridSize);
    const endNode = createNodeFromCoords(end, gridSize);

    openSet.push(startNode);

    while (openSet.length > 0) {
      let lowestIndex = 0;
      for (let i = 0; i < openSet.length; i++) {
        if (openSet[i].f < openSet[lowestIndex].f) {
          lowestIndex = i;
        }
      }

      const current = openSet[lowestIndex];

      if (current.x === endNode.x && current.y === endNode.y) {
        return reconstructPath(current);
      }

      openSet.splice(lowestIndex, 1);
      closedSet.push(current);

      const neighbors = getNeighbors(current, grid);

      neighbors.forEach(neighbor => {
        if (closedSet.includes(neighbor) || isNodeObstructed(neighbor, gridSize)) {
          return;
        }

        const tentativeG = current.g + 1;

        if (!openSet.includes(neighbor)) {
          openSet.push(neighbor);
        } else if (tentativeG >= neighbor.g) {
          return;
        }

        neighbor.g = tentativeG;
        neighbor.h = heuristic(neighbor, endNode);
        neighbor.f = neighbor.g + neighbor.h;
        neighbor.cameFrom = current;
      });
    }

    return '';
  }

  function createGrid(size) {
    const grid = [];
    const cols = canvas.clientWidth / size;
    const rows = canvas.clientHeight / size;

    for (let x = 0; x < cols; x++) {
      const col = [];
      for (let y = 0; y < rows; y++) {
        col.push({ x, y, f: 0, g: 0, h: 0, cameFrom: null });
      }
      grid.push(col);
    }

    return grid;
  }

  function createNodeFromCoords(coords, gridSize) {
    return {
      x: Math.floor(coords.x / gridSize),
      y: Math.floor(coords.y / gridSize),
      f: 0,
      g: 0,
      h: 0,
      cameFrom: null
    };
  }

  function getNeighbors(node, grid) {
    const neighbors = [];
    const directions = [
      { x: 0, y: -1 },
      { x: 1, y: 0 },
      { x: 0, y: 1 },
      { x: -1, y: 0 }
    ];

    directions.forEach(direction => {
      const newX = node.x + direction.x;
      const newY = node.y + direction.y;

      if (newX >= 0 && newX < grid.length && newY >= 0 && newY < grid[0].length) {
        neighbors.push(grid[newX][newY]);
      }
    });

    return neighbors;
  }

  function heuristic(node, endNode) {
    return Math.abs(node.x - endNode.x) + Math.abs(node.y - endNode.y);
  }

  function reconstructPath(node) {
    let path = '';
    let current = node;

    while (current.cameFrom) {
      path = `L ${current.x * 10} ${current.y * 10} ` + path;
      current = current.cameFrom;
    }

    path = `M ${current.x * 10} ${current.y * 10} ` + path;

    return path;
  }

  function isNodeObstructed(node, gridSize) {
    for (const n of nodes) {
      const rect = n.querySelector('rect');
      const transform = n.getAttribute('transform');
      const translate = transform.match(/translate\(([^,]+),\s*([^\)]+)\)/);
      const rectX = parseFloat(translate[1]) + parseFloat(rect.getAttribute('x'));
      const rectY = parseFloat(translate[2]) + parseFloat(rect.getAttribute('y'));
      const rectWidth = parseFloat(rect.getAttribute('width'));
      const rectHeight = parseFloat(rect.getAttribute('height'));

      const minX = Math.floor(rectX / gridSize);
      const maxX = Math.floor((rectX + rectWidth) / gridSize);
      const minY = Math.floor(rectY / gridSize);
      const maxY = Math.floor((rectY + rectHeight) / gridSize);

      if (node.x >= minX && node.x <= maxX && node.y >= minY && node.y <= maxY) {
        return true;
      }
    }

    return false;
  }

  function selectLink(link) {
    if (selectedLink) {
      selectedLink.classList.remove('selected');
    }
    selectedLink = link;
    selectedLink.classList.add('selected');
  }

  function deleteLink(link) {
    canvas.removeChild(link);
    const index = links.indexOf(link);
    if (index !== -1) {
      links.splice(index, 1);
    }
    selectedLink = null;
  }
});
